﻿using Castle.DynamicProxy;
using Core.AutoFacService.Abstract;
using Core.AutoFacService.Attributes.InjectAttributes;
using Core.AutoFacService.Attributes.InterceptorAttributes;
using Core.AutoFacService.Utils;
using Core.CacheService.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Core.AutoFacService.Interceptor.CacheLock
{
	[Component]
	public class CacheLockInterceptor : AbstractInterceptor
	{
		private readonly ICache cache;
		private const string CONST_KEY = "CacheLock";

		public CacheLockInterceptor(IEnumerable<Assembly> dlls, ICache cache) : base(dlls)
		{
			this.cache = cache;
		}

		public override IEnumerable<Type> GetInterceptorTypes()
		{
			return dlls.SelectMany(t => t.GetTypes())
				.Where(a => !string.IsNullOrEmpty(a.Namespace) && (a.Namespace.StartsWith("Services") || a.Namespace.StartsWith("Repository")));
		}

		public override void Intercept(IInvocation invocation)
		{
			this.ToInterceptor().Intercept(invocation);
		}

		public override void InterceptAsynchronous(IInvocation invocation)
		{
			invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
		}

		public override void InterceptAsynchronous<TResult>(IInvocation invocation)
		{
			invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
		}

		public override void InterceptSynchronous(IInvocation invocation)
		{
			var attr = GetCahceLockInfo(invocation);
			if (attr != null)
			{
				string key = AutoFacHelper.GetKey(invocation, attr, CONST_KEY);
				bool getLock = false;
				long waitMilliseconds = 0;
				while (!getLock)
				{
					getLock = cache.SetNotExists(key, key, attr.ExpireMillisecond / 1000);
					if (!getLock && waitMilliseconds <= attr.ExpireMillisecond)
					{
						waitMilliseconds += attr.RetryInterval;
						Thread.Sleep(attr.RetryInterval);
					}
					else
					{
						break;
					}
				}
				if (getLock)
				{
					try
					{
						invocation.Proceed();
					}
					catch
					{
						throw;
					}
					finally
					{
						cache.DeleteKey(key);
					}
				}
				else
				{
					throw new Exception("缓存锁超时!抛出异常终止方法执行!");
				}
			}
			else
			{
				//调用业务方法
				invocation.Proceed();
			}
		}

		private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
		{
			var attr = GetCahceLockInfo(invocation);
			if (attr != null)
			{
				string key = AutoFacHelper.GetKey(invocation, attr, CONST_KEY);
				bool getLock = false;
				long waitMilliseconds = 0;
				while (!getLock)
				{
					getLock = cache.SetNotExists(key, key, attr.ExpireMillisecond / 1000);
					if (!getLock && waitMilliseconds <= attr.ExpireMillisecond)
					{
						waitMilliseconds += attr.RetryInterval;
						Task.Delay(attr.RetryInterval).Wait();
					}
					else
					{
						break;
					}
				}
				if (getLock)
				{
					try
					{
						invocation.Proceed();
						Task<TResult> task = (Task<TResult>)invocation.ReturnValue;
						TResult result = await task;//获得返回结果
						return result;
					}
					catch
					{
						throw;
					}
					finally
					{
						cache.DeleteKey(key);
					}
				}
				else
				{
					throw new Exception("缓存锁超时!抛出异常终止方法执行!");
				}
			}
			else
			{
				//调用业务方法
				invocation.Proceed();
				Task<TResult> task = (Task<TResult>)invocation.ReturnValue;
				TResult result = await task;//获得返回结果
				return result;
			}
		}

		private async Task InternalInterceptAsynchronous(IInvocation invocation)
		{
			var attr = GetCahceLockInfo(invocation);
			if (attr != null)
			{
				string key = AutoFacHelper.GetKey(invocation, attr, CONST_KEY);
				bool getLock = false;
				long waitMilliseconds = 0;
				while (!getLock)
				{
					getLock = cache.SetNotExists(key, key, attr.ExpireMillisecond / 1000);
					if (!getLock && waitMilliseconds <= attr.ExpireMillisecond)
					{
						waitMilliseconds += attr.RetryInterval;
						Task.Delay(attr.RetryInterval).Wait();
					}
					else
					{
						break;
					}
				}
				if (getLock)
				{
					try
					{
						invocation.Proceed();
						Task task = (Task)invocation.ReturnValue;
						await task;//获得返回结果
					}
					catch
					{
						throw;
					}
					finally
					{
						cache.DeleteKey(key);
					}
				}
				else
				{
					throw new Exception("缓存锁超时!抛出异常终止方法执行!");
				}
			}
			else
			{
				//调用业务方法
				invocation.Proceed();
				Task task = (Task)invocation.ReturnValue;
				await task;//获得返回结果
			}
		}
		private CacheLockAttribute GetCahceLockInfo(IInvocation invocation)
		{
			var method = invocation.MethodInvocationTarget;
			return (CacheLockAttribute)method.GetCustomAttributes(true).Where(t => t.GetType() == typeof(CacheLockAttribute)).FirstOrDefault();
		}
	}
}
